package cn.hh.basic.service.impl;

import cn.hh.basic.constant.RegisterConstant;
import cn.hh.basic.dto.SmsDto;
import cn.hh.basic.exception.BusinessException;
import cn.hh.basic.service.VerifyCodeService;
import cn.hh.basic.util.SmsUtil;
import cn.hh.basic.util.StrUtils;
import cn.hh.basic.util.VerifyCodeUtils;
import cn.hh.user.domain.User;
import cn.hh.user.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class VerifyCodeServiceImpl implements VerifyCodeService {

    @Autowired
    private RedisTemplate redisTemplate; // 注入redis

    @Autowired
    private UserMapper userMapper; // 注入user


    @Override
    public String getImageCode(String key) {
        if (key == null) {
            throw new BusinessException("key无效");
        }
        // 1.保存到redis [key - 传过来了,value - code]
        String code = StrUtils.getComplexRandomString(4); // 调用StrUtils的静态方法getComplexRandomString()生成4位随机验证码
        redisTemplate.opsForValue().set(key, code, 3, TimeUnit.MINUTES); // 调用redis的opsForValue操作类型为value.set设置key,value,3分钟过期时间

        // 2.code进行编码base64
        String base64Str = VerifyCodeUtils.verifyCode(115, 42, code); // 调用VerifyCodeUtils的静态方法,设置宽度和长度,存入code中

        // 3.将base64编码之后的字符串响应到Controller
        return base64Str;
    }

    @Override
    public void smsCode(Map<String, String> map) {
        String phone = map.get("phone"); // 手机号
        String imageCode = map.get("imageCode"); // 图形验证码
        String imageCodeKey = map.get("imageCodeKey"); // 图形验证码对应的key值
        // 1.校验参数不能为空
        if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(imageCode) || StringUtils.isEmpty(imageCodeKey)) {
            throw new BusinessException("注册信息不能为空");
        }

        // 2.校验图形验证码
        Object obj = redisTemplate.opsForValue().get(imageCodeKey); // 从redis中获取图形验证码的key
        if (obj == null) {
            throw new BusinessException("图形验证码已过期,请重新获取输入");
        }
        // 验证码不区分大小写,用equalsIgnoreCase方法, equals是区分大小写的
        if (!obj.toString().equalsIgnoreCase(imageCode)) { // 判断redis存储的验证码是否和用户输入的验证码一致 obj对象先转成字符串
            throw new BusinessException("图形验证码输入错误,请重新输入!");
        }

        // 3.校验手机号是否被注册过
        User user = userMapper.loadByPhone(phone);
        if (user != null) { // 查询到了用户
            throw new BusinessException("手机号已被注册,请直接登录");
        }

        /**
         *  key: 手机号 + 业务键(最好定义为常量)
         *  value: code:时间戳
         */
        // 4.判断是否是第一次发送请求,或者是验证码过期 从redis中取手机号加上业务键
        Object value = redisTemplate.opsForValue().get(phone + RegisterConstant.PHONEREGISTER); // 从redis中获取: 手机号加业务键常量
        String code = null; // 先将验证码赋值为空

        // 4.1.如果取不到,说明没有发送过验证码或者是过期了, 直接生成一个新的验证码保存到redis
        if (value == null) { // 没取到
            code = StrUtils.getComplexRandomString(6); // 调用生成验证码的工具类,生成6为随机验证码
        }else {
            // 4.2.如果从redis中取到了,判断是否超过一分钟
            String redisTime = value.toString().split(":")[1];  // 取到第二个索引:时间戳
            long ltime = Long.valueOf(redisTime); // 将redisTime转换成Long类型
            // 4.2.1.如果没有超过一分钟,直接提示用户一分钟之内不能多次发送验证码
            if (System.currentTimeMillis() - ltime < 1 * 60 * 1000) { // 判断用当前时间戳 - 验证码生成的时间戳 判断是否小于1分钟
                throw new BusinessException("请勿重复获取验证码");
            } else {
                // 4.2.2.如果超过了一分钟,刷新验证码过期时间
                code = value.toString().split(":")[0]; // 超过一分钟就重新获取到存储在redis的验证码 第一个索引[0]:验证码
            }
        }


        // 4.2.3.把code存入到redis中
        // key:手机号加上业务键 value:验证码加上时间戳,设置3分钟过期时间重新存入redis中
        redisTemplate.opsForValue().set(phone + RegisterConstant.PHONEREGISTER,code + ":" + System.currentTimeMillis(),3,TimeUnit.MINUTES);
        // 5.发送短信给用户
      //  SmsUtil.sendSms(phone,"您的验证码为:" + code + ",验证码有效时间为3分钟"); // 调用工具类的静态方法,发送短信,将phone手机号和code验证码传入
        System.out.println("您的验证码为:" + code + ",验证码有效时间为3分钟");

    }

    /**
     *  手机绑定短信验证码
     * @param smsDto
     */
    @Override
    public void binderSmsCode(SmsDto smsDto) {
        String phone = smsDto.getPhone(); // 手机号
        // 1.校验参数不能为空
        if (StringUtils.isEmpty(phone)) {
            throw new BusinessException("注册信息不能为空");
        }

        // 4.判断是否是第一次发送请求,或者是验证码过期 从redis中取手机号加上业务键
        Object value = redisTemplate.opsForValue().get(phone + RegisterConstant.BINDER); // 从redis中获取: 手机号加业务键常量
        String code = null; // 先将验证码赋值为空

        // 4.1.如果取不到,说明没有发送过验证码或者是过期了, 直接生成一个新的验证码保存到redis
        if (value == null) { // 没取到
            code = StrUtils.getComplexRandomString(6); // 调用生成验证码的工具类,生成6为随机验证码
        }else {
            // 4.2.如果从redis中取到了,判断是否超过一分钟
            String redisTime = value.toString().split(":")[1];  // 取到第二个索引:时间戳
            long ltime = Long.valueOf(redisTime); // 将redisTime转换成Long类型
            // 4.2.1.如果没有超过一分钟,直接提示用户一分钟之内不能多次发送验证码
            if (System.currentTimeMillis() - ltime < 1 * 60 * 1000) { // 判断用当前时间戳 - 验证码生成的时间戳 判断是否小于1分钟
                throw new BusinessException("请勿重复获取验证码");
            } else {
                // 4.2.2.如果超过了一分钟,刷新验证码过期时间
                code = value.toString().split(":")[0]; // 超过一分钟就重新获取到存储在redis的验证码 第一个索引[0]:验证码
            }
        }


        // 4.2.3.把code存入到redis中
        // key:手机号加上业务键 value:验证码加上时间戳,设置3分钟过期时间重新存入redis中
        redisTemplate.opsForValue().set(phone + RegisterConstant.BINDER,code + ":" + System.currentTimeMillis(),3,TimeUnit.MINUTES);
        // 5.发送短信给用户
        //  SmsUtil.sendSms(phone,"您的验证码为:" + code + ",验证码有效时间为3分钟"); // 调用工具类的静态方法,发送短信,将phone手机号和code验证码传入
        System.out.println("您的验证码为:" + code + ",验证码有效时间为3分钟");


    }
}
